layui.define(["laydate", "laytpl", "table", "layer"], function (exports) {
    "use strict";
    var moduleName = 'tableEdit', _layui = layui, laytpl = _layui.laytpl
        , $ = _layui.$, laydate = _layui.laydate, table = _layui.table, layer = _layui.layer
        , selectTpl = [ //单选下拉框模板
            '<div class="layui-tableEdit-div" style="{{d.style}}">'
            , '<ul class="layui-tableEdit-ul">'
            , '{{# d.data = typeof(d.data) === "function"? d.data(d):d.data; if(d.data){ }}'
            , '{{# d.data.forEach(function(item){ }}'
            , '{{# var selectedClass = d.callbackFn(item) }}'
            , '<li class="{{ selectedClass }}" data-name="{{ item.name }}" data-value="{{ item.value }}">'
            , '<div class="layui-unselect layui-form-checkbox" lay-skin="primary"><span>{{ item.name }}</span></div>'
            , '</li>'
            , '{{# }); }}'
            , '{{# } else { }}'
            , '<li>无数据</li>'
            , '{{# } }}'
            , '</ul>'
            , '</div>'
        ].join('')
        , selectMoreTpl = [ //多选下拉框模板
            '<div class="layui-tableEdit-div" style="{{d.style}}">'
            , '<div class="layui-tableEdit-tpl">'
            , '<ul>'
            , '{{# if(d.data){ }}'
            , '{{# d.data.forEach(function(item){ }}'
            , '{{# var selectedClass = d.callbackFn(item) }}'
            , '<li class="{{ selectedClass }}" data-name="{{ item.name }}" data-value="{{ item.value }}">'
            , '<div class="layui-unselect layui-form-checkbox" lay-skin="primary"><span>{{ item.value }}</span><i class="layui-icon layui-icon-ok"></i></div>'
            , '</li>'
            , '{{# }); }}'
            , '{{# } else { }}'
            , '<li>无数据</li>'
            , '{{# } }}'
            , '</ul>'
            , '</div>'
            , '<div style="line-height: 36px;">'
            , '<div style="float: left">'
            , '<button type="button" event-type="close" class="layui-btn layui-btn-sm layui-btn-primary">关闭</button>'
            , '</div>'
            , '<div style="text-align: right">'
            , '<button event-type="confirm" type="button" class="layui-btn layui-btn-sm layui-btn-primary">确定</button>'
            , '</div>'
            , '</div>'
            , '</div>'
        ].join('');
    //组件用到的css样式
    var thisCss = [];
    thisCss.push('.layui-tableEdit-div{position:absolute;background-color:#fff;font-size:14px;border:1px solid #d2d2d2;z-index:19910908445;max-height: 252px;}');
    thisCss.push('.layui-tableEdit-tpl{max-height:216px;overflow-y:auto;}');
    thisCss.push('.layui-tableEdit-div li{line-height:36px;padding-left:5px;}');
    thisCss.push('.layui-tableEdit-div li:hover{background-color:#f2f2f2;}');
    thisCss.push('.layui-tableEdit-selected{background-color:#5FB878;}');
    thisCss.push('.layui-tableEdit-checked i{background-color:#60b979!important;}');
    thisCss.push('.layui-tableEdit-ul div{padding-left:0px!important;}');
    thisCss.push('.layui-tableEdit-input{text-align:center;position:absolute;left:0;bottom:0;width:100%;height:38px;z-index: 19910908;}');
    thisCss.push('.layui-tableEdit-add{position: absolute;right: 3px;top: 21px;margin-top: -15px;z-index: 199109084;}')
    thisCss.push('.layui-tableEdit-sub{position: absolute;left: 3px;top: 21px;margin-top: -15px;z-index: 199109084;}')
    var thisStyle = document.createElement('style');
    thisStyle.innerHTML = thisCss.join('\n'), document.getElementsByTagName('head')[0].appendChild(thisStyle);

    var configs = {
        callbacks: {}
        ,
        verify: {
            required: [
                /[\S]+/
                , '必填项不能为空'
            ]
            , phone: [
                /^1[34578]\d{9}$/
                , '请输入正确的手机号'
            ]
            , email: [
                /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/
                , '邮箱格式不正确'
            ]
            , url: [
                /(^#)|(^http(s*):\/\/[^\s]+\.[^\s]+)/
                , '链接格式不正确'
            ]
            , number: [
                /(^[-+]?\d+$)|(^[-+]?\d+\.\d+$)/
                , '只能填写数字'
            ]
            , date: [
                /^(\d{4})[-\/](\d{1}|0\d{1}|1[0-2])([-\/](\d{1}|0\d{1}|[1-2][0-9]|3[0-1]))*$/
                , '日期格式不正确'
            ]
            , identity: [
                /(^\d{15}$)|(^\d{17}(x|X|\d)$)/
                , '请输入正确的身份证号'
            ]
        }
    };

    var Class = function () { //单列模式  也就是只能new一个对象。
        var instance;
        Class = function Class() {
            return instance;
        };
        Class.prototype = this; //保留原型属性
        instance = new Class();
        instance.constructor = Class; //重置构造函数指针
        return instance
    }; //构造器
    var singleInstance = new Class();
    var inFunc = function () { singleInstance.leaveStat = false; }, outFunc = function () { singleInstance.leaveStat = true; };
    document.onclick = function () { if (singleInstance.leaveStat) singleInstance.deleteAll(); };

    //日期选择框
    Class.prototype.date = function (options) {
        var othis = this;
        othis.callback = options.callback, othis.element = options.element, othis.dateType = options.dateType;
        othis.dateType = othis.isEmpty(othis.dateType) ? "datetime" : othis.dateType;
        var that = options.element;
        if ($(that).find('input').length > 0) return;
        othis.deleteAll(), othis.leaveStat = false;
        var input = $('<input class="layui-input layui-tableEdit-input" type="text">');
        (39 - that.offsetHeight > 3) && input.css('height', '30px');
        (that.offsetHeight - 39 > 3) && input.css('height', '50px');
        $(that).append(input), input.focus();
        //日期时间选择器 (show: true 表示直接显示)
        laydate.render({
            elem: input[0], type: othis.dateType, show: true, done: function (value, date) {
                othis.deleteAll();
                if (othis.callback) othis.callback.call(that, value);
            }
        });
        $('div.layui-laydate').hover(inFunc, outFunc), $(that).hover(inFunc, outFunc);
        _layui.stope();
    };

    //输入框
    Class.prototype.input = function (options) {
        var othis = this;
        othis.callback = options.callback, othis.element = options.element;
        othis.oldValue = options.oldValue;
        othis.oldValue = othis.oldValue ? othis.oldValue : '';
        var that = options.element;
        if ($(that).find('input').length > 0) return;
        othis.deleteAll(), othis.leaveStat = false;
        var input = $('<input class="layui-input layui-tableEdit-input" style="z-index: 99999999999"  type="text">');
        (39 - that.offsetHeight > 3) && input.css('height', '30px');
        (that.offsetHeight - 39 > 3) && input.css('height', '50px');
        input.val(othis.oldValue);
        $(that).append(input), input.focus();
        input.click(function (e) {
            _layui.stope(e);
        });
        input.bind('change', function (e) { othis.callback.call(othis.element, this.value) });
        $(that).hover(inFunc, outFunc);
        _layui.stope();
    };

    //带加号和减号的输入框(只支持输入数字)
    Class.prototype.signedInput = function (options) {
        var othis = this;
        othis.callback = options.callback, othis.element = options.element;
        othis.oldValue = options.oldValue;
        othis.oldValue = othis.oldValue ? othis.oldValue : '';
        var that = options.element;
        if ($(that).find('input').length > 0) return;
        othis.deleteAll(), othis.leaveStat = false;
        var thisWidth = that.offsetWidth - 49;
        var input = $('<input class="layui-input layui-tableEdit-input" style="left: 24px;width: ' + thisWidth + 'px" type="text">');//
        var leftBtn = $('<button type="button" class="layui-btn layui-btn-sm layui-tableEdit-sub"><i class="layui-icon layui-icon-subtraction" style="margin-top:-14px!important;position: absolute;left:2px!important"></i></button>');
        var rightBtn = $('<button type="button" class="layui-btn layui-btn-sm layui-tableEdit-add"><i class="layui-icon layui-icon-addition" style="margin-top:-14px!important;position: absolute;right:-1px!important"></i></button>');
        if (39 - that.offsetHeight > 3) {
            input.css('height', '30px'); leftBtn.css('top', '16px'); rightBtn.css('top', '16px');
        }
        if (that.offsetHeight - 39 > 3) {
            input.css('height', '50px'); leftBtn.css('top', '25px'); rightBtn.css('top', '25px');
        }
        input.val(othis.oldValue);
        $(that).append(leftBtn); leftBtn.find('i').html('');
        $(that).append(input), input.focus(); $(that).append(rightBtn); rightBtn.find('i').html('');
        input.click(function (e) {
            _layui.stope(e);
        });
        input.bind('change', function (e) { othis.callback.call(othis.element, this.value) });
        $(that).hover(inFunc, outFunc);
        _layui.stope();
        $(that).find('button.layui-tableEdit-sub,button.layui-tableEdit-add').bind('click', function () {
            var input = $(that).find('input.layui-tableEdit-input');
            var val = input.val();
            if (!val || val.length <= 0) val = 0;
            val = parseInt(val);
            if ($(this).hasClass('layui-tableEdit-add')) {
                ++val; input.val(val);
                othis.callback.call(othis.element, val)
            } else {
                --val; input.val(val);
                othis.callback.call(othis.element, val)
            }
        })
    };

    //判断是否为空函数
    Class.prototype.isEmpty = function (dataStr) { return typeof dataStr === 'undefined' || dataStr === null || dataStr.length <= 0; };

    //生成下拉框函数入口
    Class.prototype.register = function (options) {
        var othis = this;
        othis.enabled = options.enabled, othis.callback = options.callback;
        othis.data = options.data, othis.element = options.element;
        othis.selectedData = options.selectedData;
        var that = othis.element;
        if ($(that).find('input.layui-tableEdit-input')[0]) return;
        othis.deleteAll(), othis.leaveStat = false;
        var input = $('<input class="layui-input layui-tableEdit-input" placeholder="请选择">')
            , tableEdit = $('<div class="layui-tableEdit"></div>')
            , tableBody = $(that).parents('div.layui-table-body')
            , tablePage = $(that).parents('div.layui-table-box').eq(0).next();
        (39 - that.offsetHeight > 3) && input.css('height', '30px');
        (that.offsetHeight - 39 > 3) && input.css('height', '50px');
        tableEdit.append(input), $(that).append(tableEdit), input.focus();
        var thisY = input[0].getBoundingClientRect().top //输入框y坐标
            , thisHeight = ((39 - that.offsetHeight > 3) ? 30 : input[0].offsetHeight) //输入框高度
            , thisHeight = ((that.offsetHeight - 39 > 3) ? 50 : thisHeight)
            , thisWidth = input[0].offsetWidth //输入框宽度
            , elemY = that.getBoundingClientRect().top //输入框y坐标
            , tableBodyY = tableBody[0].getBoundingClientRect().top
            , pageY = tablePage[0].getBoundingClientRect().top
            , tableBodyHeight = tableBody.height() //表格高度
            , isType = thisY - tableBodyY > 0.8 * tableBodyHeight
            , type = isType ? 'top: auto;bottom: ' + (thisHeight + 2) + 'px;' : 'bottom: auto;top: ' + (thisHeight + 2) + 'px;';
        if (elemY < tableBodyY) tableBody[0].scrollTop = that.offsetTop; //调整滚动条位置
        var style = type + 'width: ' + thisWidth + 'px;left: 0px;' + (othis.enabled ? '' : 'overflow-y: auto;');
        var getClassFn = function (item) {
            if (othis.isEmpty(othis.selectedData) || othis.isEmpty(item.name)) return "";
            var selectedClass;
            if (typeof othis.selectedData === 'string' || Object.prototype.toString.call(othis.selectedData) === '[object Number]') {
                selectedClass = (item.name + "" === othis.selectedData + "") || (item.value + "" === othis.selectedData + "") ? "layui-tableEdit-selected" + (othis.enabled ? " layui-tableEdit-checked" : '') : "";
            }
            if (typeof othis.selectedData === 'object') {
                selectedClass = (item.name + "" === othis.selectedData.name + "") ? "layui-tableEdit-selected" + (othis.enabled ? " layui-tableEdit-checked" : '') : "";
            }
            if (Array.isArray(othis.selectedData)) {
                for (var i = 0; i < othis.selectedData.length; i++) {
                    selectedClass = (item.name + "" === othis.selectedData[i].name + "") ? "layui-tableEdit-selected layui-tableEdit-checked" : "";
                    if (!othis.isEmpty(selectedClass)) break;
                }
            }
            return selectedClass;
        };
        tableEdit.append(laytpl(othis.enabled ? selectMoreTpl : selectTpl).render({ data: othis.data, style: style, callbackFn: getClassFn }));
        var $tableEdit = $('div.layui-tableEdit-div')[0];
        (thisY + $tableEdit.offsetHeight + thisHeight > pageY) && !isType && (tableBody[0].scrollTop = that.offsetTop);//调整滚动条位置
        othis.events();
    };

    //删除所有下拉框和时间选择框
    Class.prototype.deleteAll = function () {
        $('div.layui-tableEdit-div,div.layui-tableEdit,div.layui-laydate,input.layui-tableEdit-input,button.layui-tableEdit-sub,button.layui-tableEdit-add').remove();
        delete this.leaveStat;//清除（离开状态属性）
    };

    //注册事件
    Class.prototype.events = function () {
        var othis = this;
        var searchFunc = function (val) { //关键字搜索
            $('div.layui-tableEdit-div li').each(function () {
                othis.isEmpty(val) || $(this).data('value').indexOf(val) > -1 ? $(this).show() : $(this).hide();
            });
        }, liClickFunc = function () { //给li元素注册点击事件
            var liArr = $('div.layui-tableEdit-div li');
            liArr.unbind('click'), liArr.bind('click', function (e) {
                _layui.stope(e);
                if (othis.enabled) {//多选
                    $(this).hasClass("layui-tableEdit-checked") ? ($(this).removeClass("layui-tableEdit-checked"),
                        $(this).removeClass("layui-tableEdit-selected"))
                        : $(this).addClass("layui-tableEdit-checked")
                } else {//单选
                    othis.deleteAll();
                    if (othis.callback) othis.callback.call(othis.element, { name: $(this).data("name"), value: $(this).data("value") });
                }
            });
        }, btnClickFunc = function () { //给button按钮注册点击事件
            $("div.layui-tableEdit-div button").bind('click', function () {
                var eventType = $(this).attr("event-type"), btn = this, dataList = new Array();
                if (eventType === 'close') singleInstance.deleteAll(); //“关闭”按钮
                if (eventType === 'confirm') { //“确定”按钮
                    $('div.layui-tableEdit-div li').each(function (e) {
                        if (!$(this).hasClass("layui-tableEdit-checked")) return;
                        dataList.push({ name: $(this).data("name"), value: $(this).data("value") });
                    });
                    othis.deleteAll();
                    if (othis.callback) othis.callback.call(othis.element, dataList);
                }
            });
        };
        //事件注册
        $(othis.element).find('input.layui-tableEdit-input').bind('input propertychange', function () { searchFunc(this.value) });
        othis.enabled ? (liClickFunc(), btnClickFunc()) : liClickFunc();
        $(othis.element).hover(inFunc, outFunc);
    };

    var AopEvent = function (cols) { this.config = { colsConfig: {} }; this.parseCols(cols) };//aop构造器
    /**
     * 解析出tableEdit组件所需要的配置信息
     * @param cols
     */
    AopEvent.prototype.parseCols = function (cols) {
        var that = this;
        cols.forEach(function (ite) {
            ite.forEach(function (item) {
                if (!item.config) return;
                that.config.colsConfig[item.field] = item.config;
            });
        });
    };
    /**
     * aop代理event
     * @param event 事件名称
     * @param callback 回调函数
     */
    AopEvent.prototype.on = function (event, callback) {
        var othis = this; othis.config.event = event, othis.config.callback = callback;
        table.on(othis.config.event, function (obj) {
            var zthis = this, field = $(zthis).data('field'), config = othis.config.colsConfig[field];
            if (!config) {
                othis.config.callback.call(zthis, obj); return;
            }
            obj.field = field;
            var callbackFn = function (res) {
                if (config.verify && !othis.verify(res, config.verify, this)) return; //验证为空
                obj.value = Array.isArray(res) ? (res.length > 0 ? res : [{ name: '', value: '' }]) : res;
                othis.config.callback.call(zthis, obj);
                if (!singleInstance.isEmpty(config.cascadeSelectField)) {
                    var csElement = $(this.parentNode).find("td[data-field='" + config.cascadeSelectField + "']");
                    $(csElement).attr("cascadeSelect-data", JSON.stringify({ data: res, field: field }));
                }
            };
            var csd = $(this).attr("cascadeSelect-data");//联动数据
            if (singleInstance.isEmpty(csd)) { //非联动事件
                if (config.type === 'select') {
                    singleInstance.register({ data: config.data, element: zthis, enabled: config.enabled, selectedData: obj.data[field], callback: callbackFn });
                } else if (config.type === 'date') {
                    singleInstance.date({ dateType: config.dateType, element: zthis, callback: callbackFn });
                } else if (config.type === 'input') {
                    singleInstance.input({ element: zthis, oldValue: obj.data[field], callback: callbackFn });
                } else if (config.type === 'signedInput') {
                    singleInstance.signedInput({ element: zthis, oldValue: obj.data[field], callback: callbackFn });
                } else othis.config.callback.call(zthis, obj);
            } else {//联动事件
                if (config.type === 'date') return;
                //获取当前单元格的table表格的lay-filter属性值
                var filter = $(zthis).parents('div.layui-table-view').eq(0).prev().attr('lay-filter')
                    , rs = active.callbackFn.call(zthis, 'clickBefore(' + filter + ')', JSON.parse(csd));
                //异步操作,由使用者调用。
                //判断条件为rs为空时。
                if (singleInstance.isEmpty(rs)) {
                    active.on("async(" + filter + ")", function (result) {
                        singleInstance.register({ data: result.data, element: zthis, enabled: result.enabled, selectedData: obj.data[field], callback: callbackFn });
                    })
                } else {
                    singleInstance.register({ data: rs.data, element: zthis, enabled: rs.enabled, selectedData: obj.data[field], callback: callbackFn });
                }
            }

        });
    };

    /**
     * 验证数据是否符合要求
     * @param data 被验证数据
     * @param verify 正则参数
     * @param td 当前单元格
     * @returns {boolean} true验证通过 false验证未通过
     */
    AopEvent.prototype.verify = function (data, verify, td) {
        var verifyObj = configs.verify[verify.type];
        var verifyMsg = verify.msg;
        verifyMsg = verifyMsg ? verifyMsg : (verifyObj ? verifyObj[1] : '必填项不能为空');
        if (singleInstance.isEmpty(data)) {
            layer.tips(verifyMsg, td, { tipsMore: true });
            return false;
        }
        if (Array.isArray(data) && data.length <= 0) {
            layer.tips(verifyMsg, td, { tipsMore: true });
            return false;
        }
        if ((typeof data === 'object' && singleInstance.isEmpty(data.name))
            || data.name === 'undefined') {
            layer.tips(verifyMsg, td, { tipsMore: true });
            return false;
        }
        if (!verifyObj && !verify.regx) {
            return true;
        }
        if (verify.regx) { //自定义正则判断
            if (typeof verify.regx === "function") {//为函数时
                if (verify.regx(data)) return true;
                layer.tips(verifyMsg, td, { tipsMore: true });
                return false;
            }
            if (typeof verify.regx === "string") { //为字符串正则时
                var regx = new RegExp(verify.regx);
                if (regx.test(data)) return true;
                layer.tips(verifyMsg, td, { tipsMore: true });
                return false;
            }
            if (verify.regx.test(data)) return true; //为正则时
            layer.tips(verifyMsg, td, { tipsMore: true });
            return false;
        }
        if (!verifyObj[0].test(data)) {
            layer.tips(verifyMsg, td, { tipsMore: true });
            return false;
        }
        return true;
    };

    /**
     * 提交数据的验证
     * @param options {elem:'#test',data:[],verifyKey:'id'}
     * elem: 表格id,带井号  data: 验证数据,数组类型。
     * verifyKey: data中的元素的唯一值字段，且必须在表格中有此字段的单元格。
     * @returns {*}
     */
    AopEvent.prototype.submitValidate = function (options) {
        var that = this, failedTds = [];
        if (!options || singleInstance.isEmpty(options.verifyKey)
            || singleInstance.isEmpty(options.data)
            || singleInstance.isEmpty(options.elem)) return failedTds;
        var body = $(options.elem).next().find('div.layui-table-box div.layui-table-body tr');
        options.data.forEach(function (item) {
            for (var field in item) {
                var config = that.config.colsConfig[field];
                if (!config || !config.verify) continue;
                var verify = config.verify;
                var tds = body.find('td[data-field="' + options.verifyKey + '"]');
                var thisTd;
                tds.each(function () {
                    var text = $(this).find('div.layui-table-cell').text();
                    if (text + '' === item[options.verifyKey] + '') {
                        thisTd = $(this);
                    }
                });
                if (!thisTd) continue;
                var td = thisTd.parent().children('td[data-field="' + field + '"]');
                if (!that.verify(item[field], verify, td)) failedTds.push(td[0]);
            }
        });
        return failedTds;
    };

    var active = {
        aopObj: function (cols) { return new AopEvent(cols); },
        on: function (event, callback) {
            var filter = event.match(/\((.*)\)$/), eventName = (filter ? (event.replace(filter[0], '') + '_' + filter[1]) : event);
            configs.callbacks[moduleName + '_' + eventName] = callback;
        },
        callbackFn: function (event, params) {
            var filter = event.match(/\((.*)\)$/), eventName = (filter ? (event.replace(filter[0], '') + '_' + filter[1]) : event);
            var key = moduleName + '_' + eventName, func = configs.callbacks[key];
            if (!func) return;
            return func.call(this, params);
        }
    };

    active.on("tableEdit(getEntity)", function () {
        return singleInstance;
    });

    exports(moduleName, active);
});